package com.hllxd.hism.star.config.mvc;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * @author xiyong.lxy
 * @date 2020-11-6 7:43
 */
@Component
public class SystemConfiguration implements InitializingBean {
    private static final Logger logger = LoggerFactory.getLogger(SystemConfiguration.class);

    @Autowired
    private ApplicationContext applicationContext;
    @Override public void afterPropertiesSet() throws Exception {
        printSystemStart();
        printControllers();
    }
    private void printSystemStart() {
        logger.info("system is running...");
    }
    private void printControllers() {
        Map<String, ClazzMethod> map = loadControllers();
        for(Map.Entry<String, ClazzMethod> entry :map.entrySet()) {
            logger.info("controller: {}, {}",entry.getKey(),entry.getValue());
        }
    }

    private Map<String, ClazzMethod> loadControllers() {
        Map<String, ClazzMethod> mappingsMap = new LinkedHashMap<>();
        Map<String, Object> map = applicationContext.getBeansWithAnnotation(RestController.class);
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            String[] classMappingStrings = getReqMappingValues(value);
            ArrayList<MethodDef> list = new ArrayList<>();
            for (Method m : value.getClass().getMethods()) {
                String[] methodStrings = getReqMappingValuesFromMethod(m);
                if (! isEmpty(methodStrings)) {
                    list.add(MethodDef.build(m, methodStrings));
                }
            }
            for (String str : classMappingStrings) {
                for (MethodDef ss : list) {
                    for (String s : ss.mappings) {
                        ClazzMethod clazzMethod = new ClazzMethod();
                        clazzMethod.clazzName = key;
                        clazzMethod.clazzObject = value;
                        clazzMethod.method = ss.method;
                        //  key+":"+method
                        mappingsMap.put(str + s, clazzMethod);
                    }
                }
            }
        }
        return mappingsMap;
    }

    public static class ClazzMethod {
        public String clazzName;
        public Object clazzObject;
        public Method method;

        @Override
        public String toString() {
            return clazzName+":"+method.getName();
        }
    }

    static class MethodDef {
        public Method method;
        public String[] mappings;

        public static MethodDef build(Method method, String[] mappings) {
            MethodDef thiz = new MethodDef();
            thiz.method = method;
            thiz.mappings = mappings;
            return thiz;
        }
    }

    private static boolean isEmpty(String[] arr) {
        return (arr==null || arr.length == 0);
    }

    private String[] getReqMappingValues(Object value) {
        return toNullDefault(value.getClass().getAnnotation(RequestMapping.class), RequestMapping::value, new String[] {""});
    }

    private String[] getReqMappingValuesFromMethod(Method method) {
        return toNullDefault(method.getAnnotation(RequestMapping.class), RequestMapping::value, new String[] {});
    }

    /**
     * @param func s如果不为null,则执行此表达式
     * @param nullDefalut 如果s为null直接返回这个值
     */
    public static <T,R> R toNullDefault(T t,Function<T, R> func, R nullDefalut){
        if(null == t){
            return nullDefalut;
        }
        return func.apply(t);
    }
}
